package org.jvm.nativemethod.methods;

import org.jvm.nativemethod.NativeMethodRegister;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.heap.classLoader.KlassLoaderRegister;
import org.jvm.rtda.heap.classmember.Method;
import org.jvm.rtda.thread.Frame;
import org.jvm.rtda.thread.LocalVars;
import org.jvm.util.JavaCallUtil;
import org.jvm.util.JvmUtil;

/**
 * @author 海燕
 * @date 2023/2/26
 */
public class MyNativeMethod extends NativeMethodRegister {

    public static void init() throws NoSuchMethodException {
        register("test/MyObject", "getIntNative", "(Ljava/lang/Long;)Ljava/lang/Integer;", MyNativeMethod.class.getDeclaredMethod("getIntNative", Frame.class));
        register("java/net/URLClassLoader", "defineClass", "(Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class;", MyNativeMethod.class.getDeclaredMethod("defineClass", Frame.class));
    }

    /**
     * @param frame
     */
    public static void getIntNative(Frame frame) {
        Klass klass = KlassLoaderRegister.loadKlass("test/MyObject", frame.getThread());
        Method method = klass.getStaticMethod("getInt", "(Ljava/lang/Long;)Ljava/lang/Integer;");
        Object res = (Object) JavaCallUtil.javaCall(frame.getThread(), method, null, frame.getLocalVars().getRef(0));
        if (frame.getThread().getStack().top() != frame) {
            return;
        }
        frame.getOperandStack().pushRef(res);
    }

    /**
     * mock掉URLClassLoader的defineClass方法
     * 仅对ExtClassLoader进行mock
     *
     * @param frame
     */
    public static void defineClass(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object thisClassLoader = localVars.getThis();
        Object nameRef = localVars.getRef(1);
        String klassName = JvmUtil.javaStringToJvmString(nameRef).replace('.', '/');
        Object resource = localVars.getRef(2);
        //非ExtClassLoader时直接使用javaCall委托java加载
        if (!thisClassLoader.getKlass().getName().equals("sun/misc/Launcher$ExtClassLoader")) {
            Klass urlcl = KlassLoaderRegister.getBootKlassLoader().loadKlass("java/net/URLClassLoader");
            Method defineClassMethod = urlcl.getInstanceMethod("defineClassMock", "(Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class;");
            Object jClass = (Object) JavaCallUtil.javaCall(frame.getThread(), defineClassMethod, thisClassLoader, nameRef, resource);
            frame.getOperandStack().pushRef(jClass);
            return;
        }
        //否则mock掉ExtClassLoader，直接使用根类加载器代为加载
        Klass klass = KlassLoaderRegister.getBootKlassLoader().loadKlassWithExt(klassName);
        KlassLoaderRegister.getExtKlassLoader().addKlass(klassName, klass);
        Object jClass = klass.getjClass();
        jClass.setRefVar("classLoader", "Ljava/lang/ClassLoader;", KlassLoaderRegister.getExtKlassLoader().getJcl());
        frame.getOperandStack().pushRef(jClass);
    }
}
